/*
 * (C) Copyright 2000-2009
 * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
 *
 * SPDX-License-Identifier:	GPL-2.0+
 */
/*
 * Boot support
 */
#include "bzlib.h"
#include "zlib.h"
#include "image.h"
#include "string.h"
#include "stdlib.h"

 #define ntohl(x)	(((x & 0xFF) << 24) | ((x & 0xFF00) << 8) | ((x & 0xFF0000) >> 8)| ((x & 0xFF000000) >> 24))
 
int  gunzip (void *, int, unsigned char *, unsigned long *);

static void *zalloc(void *, unsigned, unsigned);
static void zfree(void *, void *, unsigned);

#define ulong unsigned long
#define uchar unsigned char

#define CFG_MALLOC_LEN 8192 * 1024 // 192*1024


#ifndef CFG_BOOTM_LEN
#define CFG_BOOTM_LEN	0x800000	/* use 8MByte as default max gunzip size */
#endif

image_header_t header;

int do_bootm (UINT32 u32SrcAddress,UINT32 u32DestAddress, UINT32 u32Mode)
{
	unsigned long	addr;
	unsigned long	data, len;
	unsigned long  *len_ptr;
	unsigned int	unc_len = CFG_BOOTM_LEN;
	image_header_t *hdr = &header;

	addr  =u32SrcAddress;

	sysprintf ("## Booting image at 0x%08x ...\n", addr);

	/* Copy header so we can blank CRC field for re-calculation */
	memcpy (&header, (char *)addr, sizeof(image_header_t));

	if (ntohl(hdr->ih_magic) != IH_MAGIC) {
	    {
		sysprintf ("Bad Magic Number\n");
		return 1;
	    }
	}
	else
	{
		sysprintf ("Get Magic Number\n");
		if(u32Mode)
			return 0;	
	}

	data = (ulong)&header;
	len  = sizeof(image_header_t);

	hdr->ih_hcrc = 0;

	data = addr + sizeof(image_header_t);
	len  = ntohl(hdr->ih_size);

	len_ptr = (ulong *)data;
	
	/*
	 * We have reached the point of no return: we are going to
	 * overwrite all exception vector code, so we cannot easily
	 * recover from any failures any more...
	 */
	if(hdr->ih_comp ==IH_COMP_GZIP) {
		sysprintf ("   Gzip Uncompressing to 0x%X ... ",u32DestAddress);
		if (gunzip ((void *)u32DestAddress, unc_len,
			    (uchar *)data, &len) != 0) {
			sysprintf ("GUNZIP ERROR\n");
		}	
	}
	else	
	{
		sysprintf ("Unimplemented compression type %d\n", hdr->ih_comp);
		return 1;
	}
	sysprintf ("OK\n");
	
	return 1;
}

#define	ZALLOC_ALIGNMENT	16

static void *zalloc(void *x, unsigned items, unsigned size)
{
	void *p;

	size *= items;
	size = (size + ZALLOC_ALIGNMENT - 1) & ~(ZALLOC_ALIGNMENT - 1);

	p = (void *)malloc (size);

	return (p);
}

static void zfree(void *x, void *addr, unsigned nb)
{
	free (addr);
}

#define HEAD_CRC	2
#define EXTRA_FIELD	4
#define ORIG_NAME	8
#define COMMENT		0x10
#define RESERVED	0xe0

#define DEFLATED	8

int gunzip(void *dst, int dstlen, unsigned char *src, unsigned long *lenp)
{
	z_stream s;
	int r, i, flags;

	/* skip header */
	i = 10;
	flags = src[3];
	if (src[2] != DEFLATED || (flags & RESERVED) != 0) {
		sysprintf ("Error: Bad gzipped data\n");
		return (-1);
	}
	if ((flags & EXTRA_FIELD) != 0)
		i = 12 + src[10] + (src[11] << 8);
	if ((flags & ORIG_NAME) != 0)
		while (src[i++] != 0)
			;
	if ((flags & COMMENT) != 0)
		while (src[i++] != 0)
			;
	if ((flags & HEAD_CRC) != 0)
		i += 2;
	if (i >= *lenp) {
		sysprintf ("Error: gunzip out of data in header\n");
		return (-1);
	}

	s.zalloc = zalloc;
	s.zfree = zfree;

	s.outcb = Z_NULL;

	r = inflateInit2(&s, -MAX_WBITS);
	if (r != Z_OK) {
		sysprintf ("Error: inflateInit2() returned %d\n", r);
		return (-1);
	}
	s.next_in = src + i;
	s.avail_in = *lenp - i;
	s.next_out = dst;
	s.avail_out = dstlen;
	r = inflate(&s, Z_FINISH);
	if (r != Z_OK && r != Z_STREAM_END) {
		sysprintf ("Error: inflate() returned %d\n", r);
		return (-1);
	}
	*lenp = s.next_out - (unsigned char *) dst;
	inflateEnd(&s);

	return (0);
}


#ifdef CONFIG_BZIP2
void bz_internal_error(int errcode)
{
	sysprintf ("BZIP2 internal error %d\n", errcode);
}
#endif /* CONFIG_BZIP2 */